﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CommonWFLibrary;
using System.Threading;
using System.Workflow.Runtime;
using System.Workflow.ComponentModel;

namespace CommonWFLibraryTest
{
    /// <summary>
    /// Test out the long running workflow activity
    /// </summary>
    [TestClass]
    public class t_RunLongWorkflow
    {
        public t_RunLongWorkflow()
        {
        }

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        class DelayTimer : LongRunningActivityBase
        {
            public static bool _didRun = false;
            [LongRunningMethod]
            public static void Run()
            {
                Thread.Sleep(5000);
                _didRun = true;
            }
        }

        /// <summary>
        /// Run a test, throw exceptions back to user.
        /// </summary>
        /// <param name="wfActivity"></param>
        /// <param name="dict"></param>
        private static Dictionary<string, object> RunWFByType(Type wfActivity, Dictionary<string, object> dict)
        {
            using (WorkflowRuntime runtime = new WorkflowRuntime())
            using (AutoResetEvent reset = new AutoResetEvent(false))
            {
                LongRunningActivityBase.RegisterService(runtime);
                runtime.StartRuntime();

                WorkflowInstance instance;
                instance = runtime.CreateWorkflow(wfActivity, dict);

                Exception exp = null;
                Dictionary<string, object> results = null;
                runtime.WorkflowCompleted += (o, args) =>
                {
                    if (args.WorkflowInstance.InstanceId == instance.InstanceId)
                    {
                        results = args.OutputParameters;
                        reset.Set();
                    }
                };
                runtime.WorkflowTerminated += (o, args) => { if (args.WorkflowInstance.InstanceId == instance.InstanceId) { exp = args.Exception; reset.Set(); }  };

                instance.Start();
                reset.WaitOne();

                if (exp != null)
                {
                    throw exp;
                }

                return results;
            }
        }

        [TestMethod]
        public void TestBasicWFRun()
        {
            DelayTimer._didRun = false;
            RunWFByType(typeof(RunLongWorkflow),
                new Dictionary<string, object> { { "WFType", typeof(DelayTimer) } });
            Assert.IsTrue(DelayTimer._didRun, "The delay timer doesn't seem to have run!");
        }

        /// <summary>
        /// Simple activity that gets input and output an argument
        /// </summary>
        public class SimpleActivityWithArgs : Activity
        {
            public static int _myarg = 0;

            public int InputArg
            {
                get { return (int)GetValue(InputArgProperty); }
                set { SetValue(InputArgProperty, value); }
            }

            // Using a DependencyProperty as the backing store for InputArg.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty InputArgProperty =
                DependencyProperty.Register("InputArg", typeof(int), typeof(SimpleActivityWithArgs), new PropertyMetadata(0));

            /// <summary>
            /// Simple output argument
            /// </summary>
            public int OutputArg
            {
                get { return (int)GetValue(OutputArgProperty); }
                set { SetValue(OutputArgProperty, value); }
            }

            // Using a DependencyProperty as the backing store for OutputArg.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty OutputArgProperty =
                DependencyProperty.Register("OutputArg", typeof(int), typeof(SimpleActivityWithArgs), new PropertyMetadata(0));


            protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
            {
                _myarg = InputArg;
                OutputArg = InputArg + 1;
                return ActivityExecutionStatus.Closed;
            }
        }

        [TestMethod]
        public void TestBasicWFRunWithArgs()
        {
            SimpleActivityWithArgs._myarg = 0;
            RunWFByType(typeof(RunLongWorkflow),
                new Dictionary<string, object> { { "WFType", typeof(SimpleActivityWithArgs) },
                {"WFArguments", new Dictionary<string, object> { { "InputArg", 5 } } } });
            Assert.IsTrue(SimpleActivityWithArgs._myarg == 5, "The simple argument didn't seem to work!");
        }

        [TestMethod]
        public void TestBasicWFRunWithArgsAndReturn()
        {
            SimpleActivityWithArgs._myarg = 0;
            var args = RunWFByType(typeof(RunLongWorkflow),
                new Dictionary<string, object> { { "WFType", typeof(SimpleActivityWithArgs) },
                {"WFArguments", new Dictionary<string, object> { { "InputArg", 6 } } } });
            Assert.IsTrue(SimpleActivityWithArgs._myarg == 6, "The simple argument didn't seem to work!");
            Assert.IsTrue(args != null, "Nothing came back from wf");
            Assert.IsTrue(args.ContainsKey("WFResults"), "WFResults is not in the output argument collection!");
            Dictionary<string, object> results = args["WFResults"] as Dictionary<string, object>;
            Assert.IsNotNull(results, "The output argument doesn't seem to have been there");
            Assert.IsTrue(results.ContainsKey("OutputArg"), "Output Arg not found in resulting collection");
            Assert.IsTrue(((int)results["OutputArg"]) == 7, "Resulting output argument is not 7");
        }
    }
}
